package org.example;
//多任务版本GuardedObject
//多个类之间使用GuardedObject并不方便, 因此使用解耦的中间类，来等待结果和生产结果

import lombok.extern.slf4j.Slf4j;
import org.example.util.Sleeper;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;


//例子：邮递员通过邮箱给居民送邮件(一对一)
public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++){
            new People().start();
        }
        Sleeper.sleep(1);//睡眠一秒

        for (Integer id : MailBoxes.getIds()) {
            new Postman(id, "邮件" + id).start();
        }
    }
}
@Slf4j(topic = "c.People")
class People extends Thread{

    @Override
    public void run() {
        GuardedObject guardedObject = MailBoxes.createGuardedObject();
        log.debug("开始收信,收信id:{}",guardedObject.getId());
        Object mail = guardedObject.get(5000);
        log.debug("收到信,id:{},内容:{}",guardedObject.getId(), mail);
    }
}
@Slf4j(topic = "c.Postman")
class Postman extends Thread{
    private int id;
    private String mail;

    public Postman(int id, String mail){
        this.id = id;
        this.mail = mail;
    }
    @Override
    public void run() {
        GuardedObject guardedObject = MailBoxes.getGuardedObjectById(id);
        log.debug("开始送信,送信id:{},内容:{}", id, mail);
        guardedObject.complete(mail);
    }
}

class MailBoxes{
    private static Map<Integer, GuardedObject> boxes = new Hashtable<>();

    //产生唯一性id
    private static int id = 1;

    public static synchronized int generateId(){
        return id++;
    }

    public static GuardedObject createGuardedObject(){
        GuardedObject guardedObject = new GuardedObject(generateId());
        boxes.put(guardedObject.getId(),  guardedObject);
        return guardedObject;
    }

    public static GuardedObject getGuardedObjectById(int id) {
        return boxes.remove(id);
    }
    public static Set<Integer> getIds(){
        return boxes.keySet();
    }
}

//保护性暂停 一个线程等待另一个线程的结果
//增加超时效果
class GuardedObject{

    private int id;

    private Object response;

    public GuardedObject(int id) {
        this.id = id;
    }
    public int getId(){
        return id;
    }

    //获取结果
    //timeout表示要等待多久
    public Object get(long timeout){
        synchronized (this){
            //开始时间
            long begin = System.currentTimeMillis();
            //经历时间
            long passTime = 0;
            while(response == null){
                //表示这一轮应该等待的时间
                long waitTime = timeout - passTime;
                if(waitTime <= 0){
                    break;
                }
                try {
                    this.wait(waitTime);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                passTime = System.currentTimeMillis() - begin;
            }
            return response;
        }
    }

    //生产结果
    public void complete(Object response){
        synchronized (this){
            this.response = response;
            this.notifyAll();
        }
    }
}